/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Description: Hmfs-tools resize command
 */

#include "resize_command.h"

#include <cinttypes>
#include <cstring>
#include <sstream>
#include <string_view>
#include <unistd.h>
#include <unordered_map>

#include "hmfs_common.h"
#include "hmfs_encoding.h"
#include "hmfs_log.h"
#include "resize_utils.h"
#include "securec.h"

namespace OHOS {
namespace Hmfs {
const struct option ResizeCmdParser::CMD_OPTS[] = {
    { "d", required_argument, nullptr, Option::DEBUG },
    { "V", no_argument, nullptr, Option::RVERSION},
    { "h", no_argument, nullptr, Option::HELP},
    { "f", no_argument, nullptr, Option::FORCEWRITE },
    { "t", required_argument, nullptr, Option::TARGETSECTOR},
    { "i", no_argument, nullptr, Option::EXNODEBITMAP},
    { "o", required_argument, nullptr, Option::OVERPROVISION},
    { "s", no_argument, nullptr, Option::SAFE},
    { "O", required_argument, nullptr, Option::FEATURELIST},
    { "C", required_argument, nullptr, Option::ENCODING},
    { 0, 0, 0, 0},
};

const std::string ResizeCmdParser::CMD_PARAMS = "d:fst:O:C:io:V";

ResizeCmdParser::ResizeCmdParser(CmdConfig &resizeConfig) : CmdParser(resizeConfig), resizeConfig_(resizeConfig)
{
    resizeConfig_.deviceList.resize(1);
    using namespace std::placeholders;
    RegCmdOption(Option::RVERSION, false, std::bind(&ResizeCmdParser::PrintVersion, this, _1));
    RegCmdOption(Option::HELP, false, std::bind(&ResizeCmdParser::ShowHelp, this, _1));
    RegCmdOption(Option::DEBUG, true, std::bind(&ResizeCmdParser::SetDebugLevel, this, _1));
    RegCmdOption(Option::TARGETSECTOR, true, std::bind(&ResizeCmdParser::SetTargetSectors, this, _1));
    RegCmdOption(Option::SAFE, false, std::bind(&ResizeCmdParser::SetSafe, this, _1));
    RegCmdOption(Option::FORCEWRITE, false, std::bind(&ResizeCmdParser::SetForce, this, _1));
    RegCmdOption(Option::ENCODING, true, std::bind(&ResizeCmdParser::SetEncoding, this, _1));
    RegCmdOption(Option::EXNODEBITMAP, false, std::bind(&ResizeCmdParser::SetExnodeBitmap, this, _1));
    RegCmdOption(Option::OVERPROVISION, true, std::bind(&ResizeCmdParser::SetOverProvision, this, _1));
    RegCmdOption(Option::FEATURELIST, true, std::bind(&ResizeCmdParser::SetFeatureList, this, _1));
}

uint32_t ResizeCmdParser::Parse(int argc, char *argv[])
{
    if (ParseOption(argc, argv) != ArgParseResult::OK) {
        return -1;
    }

    if (optind >= argc) {
        HMFS_ERROR("Device not specified");
        ShowCmdUsage();
        return -1;
    }
    resizeConfig_.deviceList[0] = argv[optind];
    return 0;
}

void ResizeCmdParser::ShowCmdUsage()
{
    HMFS_INFO("\nUsage: resize.hmfs [options] device\n");
    HMFS_INFO("[options]:\n");
    HMFS_INFO("  -d debug level [default:0]\n");
    HMFS_INFO("  -i extended node bitmap, node ratio is 20%% by default\n");
    HMFS_INFO("  -o overprovision percentage [default:auto]\n");
    HMFS_INFO("  -s safe resize (Does not resize metadata)\n");
    HMFS_INFO("  -t target sectors [default: device size]\n");
    HMFS_INFO("  -O feature1[,feature2,...] e.g. \"fsprojquota,fscasefold\"\n");
    HMFS_INFO("  -C [encoding[:flag1,...]] Support casefolding with optional flags\n");
    HMFS_INFO("  -V print the version number and exit\n");
    exit(1);
}

ArgParseResult ResizeCmdParser::SetDebugLevel(const std::string &argValue)
{
    if (!isDigit(argValue)) {
        HMFS_ERROR("Error: Wrong option %s\n", argValue.c_str());
        ShowCmdUsage();
    }
    resizeConfig_.debugLevel = std::stoi(argValue);
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::SetForce(const std::string &argValue)
{
    (void)argValue;
    resizeConfig_.force = 1;
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::SetSafe(const std::string &argValue)
{
    (void)argValue;
    resizeConfig_.safeResize = 1;
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::SetTargetSectors(const std::string &argValue)
{
    int ret = sscanf_s(argValue.c_str(), "%" PRIu64 "", &resizeConfig_.targetSectors);
    if (ret <= 0) {
        HMFS_ERROR("Invalid value for target sectors: %s", argValue.c_str());
    }
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::PrintVersion(const std::string &argValue)
{
    (void)argValue;
    HMFS_INFO("Info: Hmfstool version= 1.0");
    exit(0);
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::ShowHelp(const std::string &argValue)
{
    (void)argValue;
    ShowCmdUsage();
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::SetEncoding(const std::string &argValue)
{
    std::stringstream input(argValue);
    std::string encodingStr;
    std::getline(input, encodingStr, ':');
    int retValue = EncodingStrToValue(encodingStr, resizeConfig_.sEncoding);
    if (retValue) {
        HMFS_ERROR("Unknown encoding : %s", encodingStr.c_str());
        resizeConfig_.sEncoding = retValue;
    }
    std::string flagStr;
    if (std::getline(input, flagStr, ':')) {
        if (EncodingFlagStrToValue(flagStr, resizeConfig_.sEncodingFlags)) {
            HMFS_ERROR("Unknown flag : %s", flagStr.c_str());
        }
    } else {
        resizeConfig_.sEncodingFlags = GetDefaultEncodingFlag(resizeConfig_.sEncoding);
    }
    resizeConfig_.features |= HMFS_FEATURE_CASEFOLD;
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::SetOverProvision(const std::string &argValue)
{
    if(!argValue.empty()) {
        std::istringstream iss(argValue);
        double tmp;
        if(iss >> tmp) {
            resizeConfig_.newOverprovision = std::stod(argValue);
        } else {
            HMFS_ERROR("The parameter is error.");
            return ArgParseResult::ERROR;
        }
        return ArgParseResult::OK;
    } else {
        HMFS_ERROR("The parameter is not empty.");
        return ArgParseResult::ERROR;
    }
}

ArgParseResult ResizeCmdParser::SetExnodeBitmap(const std::string &argValue)
{
    (void)argValue;
    resizeConfig_.largeNatBitmap = 1;
    return ArgParseResult::OK;
}

std::vector<std::string> ResizeCmdParser::SplitStringList(const std::string &srcString, char delimiter)
{
    std::vector<std::string> list;
    std::stringstream strStream(srcString);
    std::string subString;
    while (std::getline(strStream, subString, delimiter)) {
        size_t start = subString.find_first_not_of(" ");
        if (start == std::string::npos) {
            continue;
        }
        size_t end = subString.find_last_not_of(" ");
        list.emplace_back(subString.substr(start, end - start + 1));
    }
    return list;
}

ArgParseResult ResizeCmdParser::SetFeatureList(const std::string &argValue)
{
    std::unordered_map<std::string, uint32_t> featureTable = {
        {"encrypt",                HMFS_FEATURE_ENCRYPT},
        {"extra_attr",             HMFS_FEATURE_EXTRA_ATTR},
        {"project_quota",          HMFS_FEATURE_PRJQUOTA},
        {"inode_checksum",         HMFS_FEATURE_INODE_CHKSUM},
        {"flexible_inline_xattr",  HMFS_FEATURE_FLEXIBLE_INLINE_XATTR},
        {"quota",                  HMFS_FEATURE_QUOTA_INO},
        {"inode_crtime",           HMFS_FEATURE_INODE_CRTIME},
        {"lost_found",             HMFS_FEATURE_LOST_FOUND},
        {"verity",                 HMFS_FEATURE_VERITY},
        {"sb_checksum",            HMFS_FEATURE_SB_CHKSUM},
        {"casefold",               HMFS_FEATURE_CASEFOLD},
        {"compression",            HMFS_FEATURE_COMPRESSION},
        {"ro",                     HMFS_FEATURE_RO},
    };

    std::vector<std::string> featureList = SplitStringList(argValue, ',');
    for (auto &feature : featureList) {
        HMFS_DEBUG("-O featureTrim: %s", feature.c_str());
        auto it = featureTable.find(feature);
        if (it == featureTable.end()) {
            HMFS_ERROR("Wrong features: %s", feature.c_str());
            return ArgParseResult::ERROR;
        }
        resizeConfig_.features |= it->second;
    }
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::OptionAProcess(const std::string &argValue)
{
    auto value = std::stoi(argValue);
    resizeConfig_.heapBasedAllocation = (value != 0);
    return ArgParseResult::OK;
}
} // namespace Hmfs
} // namespace OHOS